/*
 * Copyright 2021-2022 Open Kunlun Technology <https://www.openkunlun.io>
 */

package io.openkunlun.scaladsl.server

import akka.actor.ActorSystem
import akka.event.Logging
import akka.grpc.scaladsl.Metadata
import com.google.protobuf.empty.Empty
import io.openkunlun.scaladsl.context.AkkaGrpcContext
import io.openkunlun.scaladsl.util.Strings
import io.openkunlun.scaladsl.v1.HealthCheckResponse.ServingStatus
import io.openkunlun.scaladsl.v1._

import scala.concurrent.{ ExecutionContext, Future }

/**
 * @author ericxin.
 */
private[server] class DaprApp(system: ActorSystem)(implicit ec: ExecutionContext) extends DaprAppPowerApi {

  private val log = Logging(system, getClass)
  private val binding = DaprBinding(system)
  private val invocation = DaprInvocation(system)
  private val contextStorage = AkkaGrpcContext(system)

  contextStorage.init()
  /**
   *
   * @param handler
   */
  def addHandler(handler: DaprHandler): Unit = {
    addHandlers(handler :: Nil)
  }

  /**
   *
   * @param handlers
   */
  def addHandlers(handlers: Seq[DaprHandler]): Unit = {
    handlers.foreach {
      case handler: InvocationHandler => invocation.addHandler(handler)
      case handler: BindingHandler    => binding.addHandler(handler)
      case _                          => log.warning("unknown handler type.")
    }
  }

  /**
   *
   * @param handler
   */
  def addBindingHandler(handler: BindingHandler): Unit = {
    addBindingHandlers(handler :: Nil)
  }

  /**
   *
   * @param handlers
   */
  def addBindingHandlers(handlers: Seq[BindingHandler]): Unit = {
    handlers.foreach(binding.addHandler)
  }

  /**
   *
   * @param handler
   */
  def addInvocationHandler(handler: InvocationHandler): Unit = {
    addInvocationHandlers(handler :: Nil)
  }

  /**
   *
   * @param handlers
   */
  def addInvocationHandlers(handlers: Seq[InvocationHandler]): Unit = {
    handlers.foreach(invocation.addHandler)
  }

  /**
   * Invokes service method with InvokeRequest.
   */
  override def onInvoke(in: InvokeRequest, metadata: Metadata): Future[InvokeResponse] = {
    log.info("Invokes service method[{}] with metadata: {}", in.method, Strings.toString(metadata.asList.map(it => it._1 -> metadata.getText(it._1)).filter(it => Strings.nonEmpty(it._2)).map(it => it._1 -> it._2.get).toMap))
    contextStorage.store(metadata) {
      invocation.onInvoke(in, metadata)
    }
  }

  override def onHealthCheck(in: HealthCheckRequest, metadata: Metadata): Future[HealthCheckResponse] = {
    Future.successful(HealthCheckResponse(if (invocation.isHealthy || binding.isHealthy) ServingStatus.SERVING else ServingStatus.NOT_SERVING))
  }

  /**
   * Lists all input bindings subscribed by this app.
   */
  override def listBinding(in: Empty, metadata: Metadata): Future[ListBindingResponse] = {
    binding.listBinding(metadata)
  }

  /**
   * User application can save the states or send the events to the output
   * bindings optionally by returning BindingEventResponse.
   */
  override def onBindingEvent(in: BindingEventRequest, metadata: Metadata): Future[BindingEventResponse] = {
    binding.onBindingEvent(in, metadata)
  }
}

